package com.linln.common.data;

import com.linln.common.enums.ResultEnum;
import com.linln.common.enums.StatusEnum;
import com.linln.common.exception.ResultException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author 小懒虫
 * @date 2018/12/7
 */
public class QuerySpec {
    /* 查询规则 */
    public static final Long EQUAL = 0L;        // 精确查询(=)
    public static final Long LIKE = 1L;         // 模糊查询(*XX*)
    public static final Long LEFT_LIKE = 2L;    // 左模糊查询(*XX)
    public static final Long RIGHT_LIKE = 3L;   // 右模糊查询(XX*)
    public static final Long NOT_EQUAL = 4L;    // 不等于(!=)
    public static final Long GT = 5L;           // 大于(>)
    public static final Long GE = 6L;           // 大于等于(>=)
    public static final Long LT = 7L;           // 小于(<)
    public static final Long LE = 8L;           // 小于等于(<=)
    public static final Long IN = 9L;           // 多值(in)
    public static final Long BETWEEN = 10L;     // 区间查询(between)
    public static final Long NOT_IN = 11L;

    /* 字段规则列表 */
    private Map<String, Long> fieldRules;
    private Map<String, String> fieldValue;
    private String[] ignoredPaths;
    private Map<String, List<Object>> inValues;
    private Map<String, Long[]> betweenValues;
    private String status = "status";

    // 初始化方法
    private QuerySpec() {
        fieldRules = new HashMap<>();
        fieldValue = new HashMap<>();
        inValues = new HashMap<>();
        betweenValues = new HashMap<>();
    }

    // 创建查询匹配器
    public static QuerySpec matching() {
        return new QuerySpec();
    }

    // 添加匹配规则
    public QuerySpec withMatcher(String propertyPath, Long regulation) {
        fieldRules.put(propertyPath, regulation);
        return this;
    }

    // 添加匹配规则
    public QuerySpec withMatcherValue(String propertyPath, Long regulation,String value) {
        fieldRules.put(propertyPath, regulation);
        fieldValue.put(propertyPath, value);
        return this;
    }

    public QuerySpec withMatcherIn(String propertyPath, List<Object> inValueList) {
        fieldRules.put(propertyPath, IN);
        inValues.put(propertyPath, inValueList);
        return this;
    }
    public QuerySpec withMatcherNotIn(String propertyPath, List<Object> inValueList) {
        fieldRules.put(propertyPath, NOT_IN);
        inValues.put(propertyPath, inValueList);
        return this;
    }

    public QuerySpec withMatcherBetween(String propertyPath, Long x, Long y) {
        fieldRules.put(propertyPath, BETWEEN);
        betweenValues.put(propertyPath, new Long[]{x, y});
        return this;
    }

    public QuerySpec withIgnorePaths(String... ignoredPaths) {
        this.ignoredPaths = ignoredPaths;
        return this;
    }

    // 获取规则列表
    private Map<String, Long> getFieldRules() {
        return fieldRules;
    }

    private Map<String, String> getFieldValue() {
        return fieldValue;
    }

    // 获取忽略字段
    private List<String> getIgnoredPaths() {
        return Arrays.asList(ignoredPaths);
    }

    // 获取 in 查询值列表
    private Map<String, List<Object>> getInValues() {
        return inValues;
    }

    // 获取 between 查询值列表
    private Map<String, Long[]> getBetweenValues() {
        return betweenValues;
    }

    // 获取 HttpServletRequest 对象
    private HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }

    /**
     * 创建 Specification<T> 复杂查询实例，所有字段值从 GET 请求参数中获取
     * @param querySpec 匹配器
     */
    public static <T> Specification<T> of(QuerySpec querySpec) {

        // 获取查询规则、忽略字段、in 查询值、between 查询值
        Map<String, Long> fieldRules = querySpec.getFieldRules();
        //List<String> ignoredPaths = querySpec.getIgnoredPaths();
        Map<String, List<Object>> inValues = querySpec.getInValues();
        Map<String, Long[]> betweenValues = querySpec.getBetweenValues();

        // 获取 Http 请求对象
        HttpServletRequest request1 = getRequestAttributesSafely();
        HttpServletRequest request = querySpec.getRequest();
        if (request == null) {
            throw new IllegalStateException("无法获取 HttpServletRequest");
        }

        return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
            List<Predicate> preList = new ArrayList<>();
            Map<String, String> fieldValue1 = querySpec.getFieldValue();
            // 遍历规则字段，从 GET 请求中获取字段的值并构造查询条件
            for (Map.Entry<String, Long> entry : fieldRules.entrySet()) {
                String field = entry.getKey();
                Long rule = entry.getValue();
                String paramValue = request.getParameter(field);
                String value = fieldValue1.get(field);
                if(StringUtils.isNotEmpty(value)){
                    paramValue = value;
                }
                if(betweenValues.get(field)!=null || inValues.get(field)!=null){
                    paramValue = "1";
                }



                if (StringUtils.isNotEmpty(paramValue)) {
                    switch (rule.intValue()) {
                        case 0: // EQUAL
                            preList.add(cb.equal(root.get(field), paramValue));
                            break;
                        case 1: // LIKE
                            preList.add(cb.like(root.get(field), "%" + paramValue + "%"));
                            break;
                        case 2: // LEFT_LIKE
                            preList.add(cb.like(root.get(field), "%" + paramValue));
                            break;
                        case 3: // RIGHT_LIKE
                            preList.add(cb.like(root.get(field), paramValue + "%"));
                            break;
                        case 4: // NOT_EQUAL
                            preList.add(cb.notEqual(root.get(field), paramValue));
                            break;
                        case 5: // GT
                            preList.add(cb.gt(root.get(field), Long.valueOf(paramValue)));
                            break;
                        case 6: // GE
                            preList.add(cb.ge(root.get(field), Long.valueOf(paramValue)));
                            break;
                        case 7: // LT
                            preList.add(cb.lt(root.get(field), Long.valueOf(paramValue)));
                            break;
                        case 8: // LE
                            preList.add(cb.le(root.get(field), Long.valueOf(paramValue)));
                            break;
                        case 9: // IN
                            CriteriaBuilder.In<Object> in = cb.in(root.get(field));
                            List<Object> inList = inValues.get(field);
                            if (inList != null) {
                                inList.forEach(in::value);
                                preList.add(in);
                            }
                            break;
                        case 10: // BETWEEN
                            Long[] between = betweenValues.get(field);
                            if (between != null && between.length == 2) {
                                preList.add(cb.between(root.get(field), between[0], between[1]));
                            }
                            break;
                        case 11:// NOT_IN
                            CriteriaBuilder.In<Object> notIn = cb.in(root.get(field));
                            List<Object> notInList = inValues.get(field);
                            if (notInList != null) {
                                notInList.forEach(notIn::value);
                                preList.add(cb.not(notIn));
                            }
                            break;
                        default:
                            throw new IllegalArgumentException("未知的查询规则: " + rule);
                    }
                }
                // 如果参数没有传递，跳过这个查询条件
            }

            // 返回组合的查询条件
            return query.where(preList.toArray(new Predicate[0])).getRestriction();
        };
    }

    public static HttpServletRequest getRequestAttributesSafely(){
        RequestAttributes requestAttributes = null;
        HttpServletRequest request = null;
        try{
            requestAttributes = RequestContextHolder.getRequestAttributes();
            if (ObjectUtils.isEmpty(requestAttributes)){
                requestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
                RequestContextHolder.setRequestAttributes(requestAttributes);
            }
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            request = servletRequestAttributes.getRequest();
        }catch (IllegalStateException e){
            requestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
            RequestContextHolder.setRequestAttributes(requestAttributes);
        }
        return request;
    }
}
